using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ExamKing.Application.ErrorCodes;
using ExamKing.Core.Entites;
using Furion;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Quartz;
using Quartz.Spi;
using ExamKing.Application.Quartzs;
using Furion.DatabaseAccessor.Extensions;
using Furion.FriendlyException;
using Mapster;

namespace ExamKing.Application.Services
{
    /// <summary>
    /// 考试任务调度服务
    /// </summary>
    public class ExamJobService : IExamJobService, ITransient
    {
        private readonly IRepository<TbExamjobs> _repository;
        private readonly IExamService _examService;

        //调度器工厂
        private readonly ISchedulerFactory _schedulerFactory;
        private readonly IJobFactory _jobFactory;

        ///  <summary>
        /// 构造函数
        ///  </summary>
        public ExamJobService(
            ISchedulerFactory schedulerFactory,
            IJobFactory jobFactory,
            IRepository<TbExamjobs> repository
        )
        {
            _schedulerFactory = schedulerFactory;
            _jobFactory = jobFactory;
            _repository = repository;
            var examService = App.GetService<IExamService>();
            _examService = examService;
        }

        /// <summary>
        /// 恢复全部考试任务
        /// </summary>
        /// <returns></returns>
        public async Task<List<TbExamjobs>> RestartExamJobAll()
        {
            var jobs = await _repository.Entities.AsNoTracking()
                .Where(u => u.IsFinish == "0") // 0 未结束 1 已结束
                .Select(u => new TbExamjobs
                {
                    Id = u.Id,
                    ExamId = u.ExamId,
                    IsFinish = u.IsFinish,
                    StartTime = u.StartTime,
                    EndTime = u.EndTime,
                })
                .ToListAsync();
            
            foreach (var examJob in jobs)
            {
                await doExamJob(examJob);
            }

            return jobs;
        }

        public async Task<bool> doExamJob(TbExamjobs examjobs)
        {
            IScheduler scheduler = await _schedulerFactory.GetScheduler();
            scheduler.JobFactory = _jobFactory;

            var jobKey = "job_" + examjobs.ExamId;
            var triggerKey = "trigger_" + examjobs.ExamId;

            IJobDetail startjob = JobBuilder.Create<StartExamJob>()
                .UsingJobData("jobId", examjobs.Id)
                .StoreDurably(true)
                .RequestRecovery(true)
                .WithIdentity(jobKey, "start_exams")
                .Build();

            ITrigger startTrigger = TriggerBuilder.Create()
                .WithIdentity(triggerKey, "start_exams")
                .StartAt(examjobs.StartTime)
                .Build();

            await scheduler.ScheduleJob(startjob, startTrigger);

            IJobDetail finshjob = JobBuilder.Create<FinshExamJob>()
                .UsingJobData("jobId", examjobs.Id)
                .StoreDurably(true)
                .RequestRecovery(true)
                .WithIdentity(jobKey, "finsh_exams")
                .Build();

            ITrigger finshTrigger = TriggerBuilder.Create()
                .WithIdentity(triggerKey, "finsh_exams")
                .StartAt(examjobs.EndTime)
                .Build();

            await scheduler.ScheduleJob(finshjob, finshTrigger);
            return true;
        }

        /// <summary>
        /// 新增考试任务
        /// </summary>
        /// <param name="examjob"></param>
        /// <returns></returns>
        public async Task AddExamJob(TbExamjobs examjob)
        {
            var jobEntity = await _repository.InsertNowAsync(examjob);
            await doExamJob(jobEntity.Entity);
        }

        /// <summary>
        /// 更新考试任务
        /// </summary>
        /// <param name="id"></param>
        /// <param name="startTime"></param>
        /// <returns></returns>
        public async Task<bool> UpdateExamJob(TbExamjobs examJob)
        {
            var oldExamJob = await _repository.Entities.AsNoTracking()
                .FirstOrDefaultAsync(u => u.ExamId == examJob.ExamId);
            
            if (oldExamJob == null)
            {
                return false;
            }

            examJob.Id = oldExamJob.Id;

            await DelStartExamJob(examJob.ExamId.ToString());
            await DelFinshExamJob(examJob.ExamId.ToString());
            
            var examJobEntity = await examJob.UpdateNowAsync();
            
            await doExamJob(examJobEntity.Entity);
            return true;
        }

        /// <summary>
        /// 执行完成考试任务
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<bool> ExecuteFinshExamJob(int id)
        {
            var examJob = await _repository.Entities.AsNoTracking()
                .Select(u => new TbExamjobs
                {
                    Id = u.Id,
                    ExamId = u.ExamId,
                    IsFinish = u.IsFinish,
                    StartTime = u.StartTime,
                    EndTime = u.EndTime,
                    Exam = new TbExam
                    {
                        Id = u.Exam.Id,
                        ExamName = u.Exam.ExamName,
                        StartTime = u.Exam.StartTime,
                        EndTime = u.Exam.EndTime,
                        Duration = u.Exam.Duration,
                        IsEnable = u.Exam.IsEnable,
                        IsFinish = u.Exam.IsFinish,
                        Classes = u.Exam.Classes.Select(
                            x => new TbClass
                            {
                                Id = x.Id,
                                Students = x.Students.Select(
                                    s => new TbStudent
                                    {
                                        Id = s.Id,
                                    }).ToList()
                            }).ToList()
                    }
                })
                .FirstOrDefaultAsync(u => u.Id == id);

            if (examJob == null)
            {
                throw Oops.Oh(ExamErrorCodes.s1905);
            }

            // 计算全部考生成绩
            foreach (var classes in examJob.Exam.Classes)
            {
                foreach (var stu in classes.Students)
                {
                    try
                    {
                        // 计算学生考试成绩
                        await _examService.SubmitExamByStudent(examJob.ExamId, stu.Id);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                        continue;
                    }
                }
            }

            // 结束任务调度
            examJob.IsFinish = "1";
            await _repository.UpdateIncludeNowAsync(examJob, new[] { "IsFinish" });
            // 结束考试
            await _examService.FinshExamById(examJob.ExamId);

            // 移除任务
            await DelFinshExamJob(examJob.ExamId.ToString());

            return true;
        }

        public async Task<bool> DelStartExamJob(string examId)
        {
            var jobKey = "job_" + examId;
            JobKey sjk = new JobKey(jobKey, "start_exams");
            IScheduler scheduler = await _schedulerFactory.GetScheduler();
            scheduler.JobFactory = _jobFactory;
            await scheduler.DeleteJob(sjk);
            return true;
        }

        public async Task<bool> DelFinshExamJob(string examId)
        {
            // 移除任务
            var jobKey = "job_" + examId;
            JobKey fjk = new JobKey(jobKey, "finsh_exams");
            IScheduler scheduler = await _schedulerFactory.GetScheduler();
            scheduler.JobFactory = _jobFactory;
            await scheduler.DeleteJob(fjk);
            return true;
        }

        /// <summary>
        /// 执行开始考试任务
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<bool> ExecuteStartExamJob(int id)
        {
            var examJob = await _repository.Entities.AsNoTracking()
                .Select(u => new TbExamjobs
                {
                    Id = u.Id,
                    ExamId = u.ExamId,
                    IsFinish = u.IsFinish,
                    StartTime = u.StartTime,
                    EndTime = u.EndTime
                })
                .FirstOrDefaultAsync(u => u.Id == id);

            if (examJob == null)
            {
                throw Oops.Oh(ExamErrorCodes.s1905);
            }

            await _examService.EnableExamById(examJob.ExamId);

            // 移除任务
            await DelStartExamJob(examJob.ExamId.ToString());

            return true;
        }
    }
}